using System;
using System.Collections;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Messaging;

using Pegasus.Diagnostics;
using Pegasus.Runtime.Remoting.Messaging;
using Pegasus.Runtime.Serialization.Formatters.Xml;
using Pegasus.Log4Net;

namespace Pegasus.Runtime.Remoting.Channels
{
    /// <summary>
    /// Provides the implementation for a client formatter sink that uses the XmlFormatter2 class. 
    /// </summary>
    public class XmlClientFormatterSink : IClientFormatterSink
    {
        private static ILog s_logger = LogManager.GetLogger( typeof( XmlClientFormatterSink ) );

        // Local Instance Values
        private IClientChannelSink m_nextChannelSink;

        /// <summary>
        /// Initializes a new instance of the <see cref="XmlClientFormatterSink"/> class.
        /// </summary>
        /// <param name="nextSink">The next sink in the skink chain.</param>
        public XmlClientFormatterSink( IClientChannelSink nextSink )
        {
            // Check Parameters
			ParamCode.AssertNotNull( nextSink, "nextSink" );

            m_nextChannelSink = nextSink;
        }

        /// <summary>
        /// Gets a dictionary through which properties on the sink can be accessed.
        /// </summary>
        /// <value></value>
        /// <returns>A dictionary through which properties on the sink can be accessed, or null if the channel sink does not support properties.</returns>
        /// <exception cref="T:System.Security.SecurityException">The immediate caller does not have infrastructure permission. </exception>
        /// <PermissionSet><IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="Infrastructure"/></PermissionSet>
        public IDictionary Properties
        {
            get
            {
                return null;
            }
        }

        /// <summary>
        /// Gets the next message sink in the sink chain.
        /// </summary>
        /// <value></value>
        /// <returns>The next message sink in the sink chain.</returns>
        /// <exception cref="T:System.Security.SecurityException">The immediate caller makes the call through a reference to the interface and does not have infrastructure permission. </exception>
        /// <PermissionSet><IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="Infrastructure"/></PermissionSet>
        public IMessageSink NextSink
        {
            get
            {
                throw new NotSupportedException( "Property NextSink is not supported" );
            }
        }

        /// <summary>
        /// Gets the next client channel sink in the client sink chain.
        /// </summary>
        /// <value></value>
        /// <returns>The next client channel sink in the client sink chain.</returns>
        /// <exception cref="T:System.Security.SecurityException">The immediate caller does not have infrastructure permission. </exception>
        /// <PermissionSet><IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="Infrastructure"/></PermissionSet>
        public IClientChannelSink NextChannelSink
        {
            get
            {
                return m_nextChannelSink;
            }
        }

		/// <summary>
		/// Requests asynchronous processing of a method call on the current sink.
		/// </summary>
		/// <param name="sinkStack">A stack of channel sinks that called this sink.</param>
		/// <param name="msg">The message to process.</param>
		/// <param name="headers">The headers to add to the outgoing message heading to the server.</param>
		/// <param name="stream">The stream headed to the transport sink.</param>
		/// <exception cref="T:System.Security.SecurityException">The immediate caller does not have infrastructure permission. </exception>
		public void AsyncProcessRequest( IClientChannelSinkStack sinkStack, IMessage msg, ITransportHeaders headers, Stream stream )
		{
			throw new NotSupportedException( "AsyncProcessMessage method is not supported" );
		}

		/// <summary>
		/// Returns the <see cref="T:System.IO.Stream"></see> onto which the provided message is to be serialized.
		/// </summary>
		/// <param name="msg">The <see cref="T:System.Runtime.Remoting.Messaging.IMethodCallMessage"></see> containing details about the method call.</param>
		/// <param name="headers">The headers to add to the outgoing message heading to the server.</param>
		/// <returns>
		/// The <see cref="T:System.IO.Stream"></see> onto which the provided message is to be serialized.
		/// </returns>
		/// <exception cref="T:System.Security.SecurityException">The immediate caller does not have infrastructure permission. </exception>
		public Stream GetRequestStream( IMessage msg, ITransportHeaders headers )
		{
			throw new NotSupportedException( "GetRequestStream method is not supported" );
		}

		/// <summary>
		/// Requests message processing from the current sink.
		/// </summary>
		/// <param name="msg">The message to process.</param>
		/// <param name="requestHeaders">The headers to add to the outgoing message heading to the server.</param>
		/// <param name="requestStream">The stream headed to the transport sink.</param>
		/// <param name="responseHeaders">When this method returns, contains a <see cref="T:System.Runtime.Remoting.Channels.ITransportHeaders"></see> interface that holds the headers that the server returned. This parameter is passed uninitialized.</param>
		/// <param name="responseStream">When this method returns, contains a <see cref="T:System.IO.Stream"></see> coming back from the transport sink. This parameter is passed uninitialized.</param>
		/// <exception cref="T:System.Security.SecurityException">The immediate caller does not have infrastructure permission. </exception>
		public void ProcessMessage( IMessage msg, ITransportHeaders requestHeaders, Stream requestStream, out ITransportHeaders responseHeaders, out Stream responseStream )
		{
			throw new NotSupportedException( "ProcessMessage method is not supported" );
		}

		/// <summary>
		/// Requests asynchronous processing of a response to a method call on the current sink.
		/// </summary>
		/// <param name="sinkStack">A stack of sinks that called this sink.</param>
		/// <param name="state">Information generated on the request side that is associated with this sink.</param>
		/// <param name="headers">The headers retrieved from the server response stream.</param>
		/// <param name="stream">The stream coming back from the transport sink.</param>
		/// <exception cref="T:System.Security.SecurityException">The immediate caller does not have infrastructure permission. </exception>
		public void AsyncProcessResponse( IClientResponseChannelSinkStack sinkStack, object state, ITransportHeaders headers, Stream stream )
		{
			throw new NotImplementedException( "AsyncProcessResponse method has not been implemented" );
		}

        /// <summary>
        /// Asynchronously processes the given message.
        /// </summary>
        /// <param name="msg">The message to process.</param>
        /// <param name="replySink">The reply sink for the reply message.</param>
        /// <returns>
        /// Returns an <see cref="T:System.Runtime.Remoting.Messaging.IMessageCtrl"></see> interface that provides a way to control asynchronous messages after they have been dispatched.
        /// </returns>
        /// <exception cref="T:System.Security.SecurityException">The immediate caller makes the call through a reference to the interface and does not have infrastructure permission. </exception>
        public IMessageCtrl AsyncProcessMessage( IMessage msg, IMessageSink replySink )
        {
			throw new NotImplementedException( "AsyncProcessMessage method has not been implemented" );
		}
        
        /// <summary>
        /// Synchronously processes the given message.
        /// </summary>
        /// <param name="msg">The message to process.</param>
        /// <returns>
        /// A reply message in response to the request.
        /// </returns>
        /// <exception cref="T:System.Security.SecurityException">The immediate caller makes the call through a reference to the interface and does not have infrastructure permission. </exception>
        public IMessage SyncProcessMessage( IMessage msg )
        {
            try
            {
                // Get the request stream from the channle or setup a memory stream for serialization.
                TransportHeaders requestHeaders = new TransportHeaders();
                Stream requestStream = m_nextChannelSink.GetRequestStream( msg, requestHeaders );
                if( requestStream == null )
                {
                    requestStream = new MemoryStream();
                }

                // Setup the formatter
                XmlFormatter2 formatter = new XmlFormatter2( new PegasusRemotingSurrogateSelector(), new StreamingContext( StreamingContextStates.Other ) );

                // Create a method can and serialize it to the request stream.
                PegasusMethodCall methodCall = new PegasusMethodCall( msg );
                formatter.Serialize( requestStream, methodCall );

                // Let channel sink chain process the message.
                ITransportHeaders responseHeaders = null;
                Stream responseStream = null;

                m_nextChannelSink.ProcessMessage( methodCall, requestHeaders, requestStream, out responseHeaders, out responseStream );

                // Deserialze the response stream.
                formatter.SurrogateSelector = null;
                PegasusMethodCall msgResponse = (PegasusMethodCall) formatter.Deserialize( responseStream );
                return msgResponse;
            }
            catch( Exception ex )
            {
                s_logger.Error( "DIED", ex );
                throw;
            }
        }

    }
}
